Imports System.Collections.Generic
Imports System.Text
Imports System.Reflection
Imports System.Reflection.Emit

Namespace Core
	Friend Delegate Sub QuickReadDelegate(encoding As IHasEncoding, bytes As [Byte](), record As Object)
	Friend Delegate Sub QuickWriteDelegate(encoding As IHasEncoding, bytes As [Byte](), record As Object)
	Friend Delegate Function QuickCompareDelegate(encoding As IHasEncoding, record1 As Object, record2 As Object) As Integer

	Friend NotInheritable Class QuickSerializer
		Friend mColumns As List(Of ColumnDefinition)
		Friend mRecordWidth As Integer
		Friend mVersion As DbfVersion
		Friend mIsFixedRecordWidth As Boolean
		Friend mQuickReadMethod As QuickReadDelegate
		Friend mQuickWriteMethod As QuickWriteDelegate
		Friend mQuickCompareMethod As QuickCompareDelegate
		Friend mCreateColumns As Boolean

		Private mIlRead As ILGenerator
		Private mIlWrite As ILGenerator
		Private mIlCompare As ILGenerator
		Friend mIgnoreMissingFields As Boolean

		Public Sub New(recordType As Type, version As DbfVersion, columns As List(Of ColumnDefinition), recordWidth As Integer, ignoreMissingFields As Boolean, setOffset As Boolean)
			mVersion = version
			If columns Is Nothing Then
				columns = New List(Of ColumnDefinition)()
				mCreateColumns = True
			End If
			mColumns = columns
			mIgnoreMissingFields = ignoreMissingFields

			mRecordWidth = 0
			' dbf use a character to specify deleted record
			mIsFixedRecordWidth = True

            Dim readMethod As DynamicMethod = New DynamicMethod("__readRecord", Nothing, New Type() {GetType(IHasEncoding), GetType([Byte]()), GetType(Object)}, recordType, True)

			mIlRead = readMethod.GetILGenerator()

            Dim writeMethod As DynamicMethod = New DynamicMethod("__writeRecord", Nothing, New Type() {GetType(IHasEncoding), GetType([Byte]()), GetType(Object)}, recordType, True)

			mIlWrite = writeMethod.GetILGenerator()

            Dim compareMethod As DynamicMethod = New DynamicMethod("__compareRecord", GetType(Integer), New Type() {GetType(IHasEncoding), GetType(Object), GetType(Object)}, recordType, True)

			mIlCompare = compareMethod.GetILGenerator()

			EnumerateFields(recordType)

			Dim currentOffset As Integer = 0
			Dim columnIndex As Integer = 0
            For Each cd As ColumnDefinition In mColumns
                If setOffset Then
                    cd.ExtendedProperties("OffSet") = currentOffset
                    currentOffset += CInt(cd.ExtendedProperties("ColumnWidth"))
                End If
                cd.Index = System.Math.Max(System.Threading.Interlocked.Increment(columnIndex), columnIndex - 1)
                EmitColumnCode(cd)
            Next
			mRecordWidth = currentOffset

			mIlRead.Emit(OpCodes.Ret)
			mIlWrite.Emit(OpCodes.Ret)

			mIlCompare.Emit(OpCodes.Ldc_I4_0)
			' if not return yet, we can say that the records are equal
			mIlCompare.Emit(OpCodes.Ret)

			mQuickReadMethod = DirectCast(readMethod.CreateDelegate(GetType(QuickReadDelegate)), QuickReadDelegate)
			mQuickWriteMethod = DirectCast(writeMethod.CreateDelegate(GetType(QuickWriteDelegate)), QuickWriteDelegate)
			mQuickCompareMethod = DirectCast(compareMethod.CreateDelegate(GetType(QuickCompareDelegate)), QuickCompareDelegate)

			If recordWidth > 0 Then
				mRecordWidth = recordWidth
			End If
		End Sub

		Private Sub EnumerateFields(recordType As Type)
			If recordType Is Nothing Then
				Return
			End If
			' we first enumerate the inherited classes
			EnumerateFields(recordType.BaseType)

            Dim atts As Object() = recordType.GetCustomAttributes(GetType(RecordAttribute), False)
            Dim recordAttribute As RecordAttribute = (If(atts.Length = 1, DirectCast(atts(0), RecordAttribute), New RecordAttribute()))

			' We enumerate All Public and NonPublic fields to get the 
			' columns that explicitely have the [Column(...)] attribute.
            For Each fieldInfo As Reflection.FieldInfo In recordType.GetFields(BindingFlags.Instance Or BindingFlags.[Public] Or BindingFlags.NonPublic Or BindingFlags.DeclaredOnly)
                AddColumn(fieldInfo, recordAttribute)
            Next
		End Sub

		Private Sub AddColumn(fieldInfo As FieldInfo, recordAttribute As RecordAttribute)
			' not sure whether it is more convenient for the user to inherit or not
            Dim atts() As Object = fieldInfo.GetCustomAttributes(GetType(ColumnAttribute), True)

			Dim columnAttribute As ColumnAttribute = Nothing

			If atts.Length = 0 Then
                Dim fm As FieldMapping = recordAttribute.FieldMapping
				If fieldInfo.IsPublic Then
					If (fm And FieldMapping.PublicFields) = 0 Then
						Return
					End If
				Else
					If (fm And FieldMapping.PrivateFields) = 0 Then
						Return
					End If
				End If
			Else
				columnAttribute = DirectCast(atts(0), ColumnAttribute)
			End If
			Dim cd As ColumnDefinition = Nothing
			If mCreateColumns Then
                cd = New ColumnDefinition(mVersion, fieldInfo, columnAttribute)
                mColumns.Add(cd)
			ElseIf columnAttribute IsNot Nothing AndAlso columnAttribute.AdvancedType = AdvancedColumnType.DELETED_FLAG Then
                cd = New ColumnDefinition(mVersion, fieldInfo, columnAttribute)
                mColumns.Insert(0, cd)
			Else
				Dim found As Boolean = False
				For i As Integer = 0 To mColumns.Count - 1
					cd = mColumns(i)
                    If fieldInfo.Name = cd.ColumnName Then
                        cd.mFieldInfo = fieldInfo
                        found = True
                        Exit For
                    End If
				Next
				If found = False AndAlso mIgnoreMissingFields = False Then
					Throw New Exception(String.Format("Column {0} was not found in existing DBF file", fieldInfo.Name))
				End If
			End If

            If CInt(cd.ExtendedProperties("ColumnWidth")) = 0 Then
                mIsFixedRecordWidth = False
            End If
		End Sub
        Private Sub EmitColumnCode(cd As ColumnDefinition)
            '  Ldarg_0 : DbfTableDefinitions
            '  Ldarg_1 : Byte[]
            '  Ldarg_2 : DbfRecord
            If cd.AdvancedColumnType = AdvancedColumnType.DELETED_FLAG OrElse cd.AdvancedColumnType = AdvancedColumnType.DELAYED Then
                Return
            End If
            '
            Dim readMethodName As String
            Dim writeMethodName As String
            Dim dbfType As String = (If(CType(cd.ExtendedProperties("ColumnType"), ColumnType) = ColumnType.UNKNOWN, cd.AdvancedColumnType.ToString(), cd.ExtendedProperties("ColumnType").ToString()))
            Dim nativeType As String
            Dim fieldType As Type = Nothing
            '
            fieldType = (If(cd.mFieldInfo Is Nothing, cd.NaturalFieldType(), cd.mFieldInfo.FieldType))
            nativeType = fieldType.Name
            cd.DataType = fieldType

            If fieldType.IsGenericType Then
                If fieldType.GetGenericTypeDefinition() Is GetType(Nullable(Of )) Then
                    nativeType = "Nullable" & fieldType.GetGenericArguments()(0).Name
                End If
            ElseIf fieldType.IsArray Then
                nativeType = fieldType.GetElementType().Name & "Array"
            ElseIf fieldType.IsEnum Then
                nativeType = Type.GetTypeCode(fieldType).ToString()
            End If

            If dbfType = "DATETIME" Then
                dbfType = ""
            End If

            readMethodName = "Read_" & dbfType & "_" & nativeType

            cd.readXXX = Me.[GetType]().GetMethod(readMethodName, BindingFlags.NonPublic Or BindingFlags.[Static])

            If cd.readXXX Is Nothing Then
                Throw New Exception(String.Format("Unknown Method : {0}.", readMethodName))
            End If
            writeMethodName = "Write_" & nativeType & "_" & dbfType



            cd.writeXXX = Me.[GetType]().GetMethod(writeMethodName, BindingFlags.NonPublic Or BindingFlags.[Static])

            If cd.writeXXX Is Nothing Then
                Throw New Exception(String.Format("Unknown Method : {0}.", writeMethodName))
            End If



            If cd.mFieldInfo Is Nothing Then
                'var dlg = typeof(Read_XXX_Object_Delegate<>).MakeGenericType(new Type[] { fieldType });
                'Dim dlg = GetType(Read_XXX_Object_Delegate)

                'cd.readXXXDelegate = (Read_XXX_Object_Delegate)Delegate.CreateDelegate(dlg, cd.readXXX);
                Return
            End If

            'void WriteXXX(
            mIlWrite.Emit(OpCodes.Ldarg_1)
            ' << 1:Byte[] bytes
            mIlWrite.Emit(OpCodes.Ldarg_0)
            ' << 2:dbfTable
            mIlWrite.Emit(OpCodes.Ldc_I4, cd.Index)
            ' << 3: ColumIndex
            mIlWrite.Emit(OpCodes.Ldc_I4, CInt(cd.ExtendedProperties("OffSet")))
            ' << 4:Offset // for speed
            mIlWrite.Emit(OpCodes.Ldc_I4, CInt(cd.ExtendedProperties("ColumnWidth")))
            ' << 5:Length // for speed
            mIlWrite.Emit(OpCodes.Ldarg_2)
            ' <  Record
            mIlWrite.Emit(OpCodes.Ldfld, cd.mFieldInfo)
            ' >< 5:DateTime/... value
            mIlWrite.Emit(OpCodes.[Call], cd.writeXXX)


            'void ReadXXX(
            mIlRead.Emit(OpCodes.Ldarg_2)
            ' <  Record
            mIlRead.Emit(OpCodes.Ldarg_0)
            ' << 2:dbfTable
            mIlRead.Emit(OpCodes.Ldarg_1)
            ' << 0:Byte[]
            mIlRead.Emit(OpCodes.Ldc_I4, CInt(cd.ExtendedProperties("OffSet")))
            ' << 1:Offset // for speed
            mIlRead.Emit(OpCodes.Ldc_I4, CInt(cd.ExtendedProperties("ColumnWidth")))
            ' << 2:Length // for speed
            mIlRead.Emit(OpCodes.[Call], cd.readXXX)
            mIlRead.Emit(OpCodes.Stfld, cd.mFieldInfo)
            ' >< 5:DateTime/... value
        End Sub

		Public ReadOnly Property RecordWidth() As Integer
			Get
				Return mRecordWidth
			End Get
		End Property

		Public Sub Read(encoding As IHasEncoding, bytes As Byte(), record As Object)
			mQuickReadMethod(encoding, bytes, record)
		End Sub

		Public Sub Write(encoding As IHasEncoding, bytes As Byte(), record As Object)
			' Speedup ROBS
			If TypeOf record Is Record Then
                Dim b As Byte() = DirectCast(record, Record).RecordHolder.GetCurrentBuffer(False)
				If b IsNot Nothing Then
					Buffer.BlockCopy(b, 0, bytes, 0, b.Length)
					Return
				End If
			End If
			mQuickWriteMethod(encoding, bytes, record)
		End Sub

		Public Function Compare(encoding As IHasEncoding, record1 As Object, record2 As Object) As Integer
			Return mQuickCompareMethod(encoding, record1, record2)
		End Function

		Public ReadOnly Property Columns() As List(Of ColumnDefinition)
			Get
				Return mColumns
			End Get
		End Property

		Private Shared Sub WriteIntInBytes(dstBytes As Byte(), dst As Integer, width As Integer, value As Integer, recordModified As Boolean)
            Dim str As String = value.ToString()
			If str.Length > width Then
				Throw New Exception(String.Format("Cannot write {0} in {1} digits.", value, width))
			End If
			While str.Length < width
				str = "0" & str
			End While
            Dim srcBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(str)
			WriteInBytes(dstBytes, dst, srcBytes)
		End Sub

		Private Shared Sub WriteSingleByte(dstBytes As [Byte](), offset As Integer, srcByte As [Byte])
			If dstBytes(offset) <> srcByte Then
				dstBytes(offset) = srcByte
			End If
		End Sub

		Private Shared Sub WriteInBytes(dstBytes As [Byte](), offset As Integer, srcBytes As [Byte]())
			Dim src As Integer = 0
			Dim srcLength As Integer = srcBytes.Length
			Dim dst As Integer = offset

			While src < srcLength
				If dstBytes(dst) <> srcBytes(src) Then
                    While src < srcLength
                        Try
                            dstBytes(System.Math.Max(System.Threading.Interlocked.Increment(dst), dst - 1)) = srcBytes(System.Math.Max(System.Threading.Interlocked.Increment(src), src - 1))
                        Catch
                            Exit While
                        End Try
                    End While
					Exit While
				End If
				dst += 1
				src += 1
			End While
		End Sub

		Friend Delegate Function Read_XXX_Object_Delegate(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Object

		#Region "Functions Used in Emitted Code"

		Protected Shared Function Read_DBASE3_CHARACTER_String(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As String
			Dim l As Integer = offset
			Dim r As Integer = offset + length
			Dim c As Integer = (l + r + 1) \ 2
			While r - l > 1
				If srcBytes(c) = 0 Then
					' have a 0 byte, try making it smaller
					r = c
				Else
					' have a char byte, try making it bigger
					l = c
				End If
				c = (l + r + 1) \ 2
			End While
			Return encoding.Encoding.GetString(srcBytes, offset, c - offset).TrimEnd()
		End Function



		Protected Shared Sub Write_String_DBASE3_CHARACTER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, stringValue As String)
			If [String].IsNullOrEmpty(stringValue) Then
				' Need to clear?
				If dstBytes(dst) <> 0 Then
					Array.Clear(dstBytes, dst, dstLength)
				End If
				Return
			End If
			Dim srcBytes As Byte() = encoding.Encoding.GetBytes(stringValue)
			Dim srcLength As Integer = srcBytes.Length

			If srcLength > dstLength Then
				Throw New FieldOverflowException(String.Format("Column #{0} cannot store more than {1} characters.", columnIndex + 1, dstLength))
			End If

			Buffer.BlockCopy(srcBytes, 0, dstBytes, dst, srcLength)
			If dstLength - srcLength > 0 Then
				' Need to clear?
				If dstBytes(dst + srcLength + 1) <> 0 Then
					Array.Clear(dstBytes, dst + srcLength, dstLength - srcLength)
				End If
			End If
		End Sub

		Protected Shared Function Read_BYTES_ByteArray(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As [Byte]()
			Dim result As [Byte]() = New [Byte](length - 1) {}
			Array.Copy(srcBytes, offset, result, 0, length)
			Return result
		End Function

		Protected Shared Sub Write_ByteArray_BYTES(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As [Byte]())
			Array.Copy(value, 0, dstBytes, dst, dstLength)
		End Sub

		Protected Shared Function Read_DBASE3_CHARACTER_Char(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Char
            Dim resultString As String = encoding.Encoding.GetString(srcBytes, offset, length)
			If resultString.Length = 0 Then
				Return ControlChars.NullChar
			End If
			Return resultString(0)
		End Function

		Protected Shared Sub Write_Char_DBASE3_CHARACTER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, charValue As Char)
            Dim stringValue As String = charValue.ToString()
            Dim srcBytes As Byte() = encoding.Encoding.GetBytes(stringValue)
			WriteInBytes(dstBytes, dst, srcBytes)
		End Sub

		Protected Shared Function Read_DATE_YMD_DateTime(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As DateTime
		'ref string stringValue
			Dim y As Integer, m As Integer, d As Integer

			y = 1900 + srcBytes(offset)
			m = srcBytes(offset + 1)
			If m >= 1 AndAlso m <= 12 Then
				d = srcBytes(offset + 2)

				If d >= 1 AndAlso d <= 31 Then
					Try
						Return New DateTime(y, m, d)
							' ignore
					Catch generatedExceptionName As Exception
					End Try
				End If
			End If
			Return New DateTime()
		End Function

		Protected Shared Sub Write_DateTime_DATE_YMD(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, dateValue As DateTime)
			WriteSingleByte(dstBytes, dst, CType(dateValue.Year - 1900, [Byte]))
			WriteSingleByte(dstBytes, dst + 1, CType(dateValue.Month, [Byte]))
			WriteSingleByte(dstBytes, dst + 2, CType(dateValue.Day, [Byte]))
		End Sub

		Protected Shared Function Read_DBASE3_DATE_NullableDateTime(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As System.Nullable(Of DateTime)
		'ref string stringValue
			Dim year As System.Nullable(Of Integer) = Read_DBASE3_NUMBER_NullableInt32(encoding, srcBytes, offset, 4)
			Dim month As System.Nullable(Of Integer) = Read_DBASE3_NUMBER_NullableInt32(encoding, srcBytes, offset + 4, 2)
			Dim day As System.Nullable(Of Integer) = Read_DBASE3_NUMBER_NullableInt32(encoding, srcBytes, offset + 6, 2)
			'
			If year.HasValue AndAlso month.HasValue AndAlso day.HasValue Then
				Return New DateTime(year.Value, month.Value, day.Value)
			Else
				Return Nothing
			End If
		End Function

		Protected Shared Sub Write_NullableDateTime_DBASE3_DATE(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, dateValue As System.Nullable(Of DateTime))
			Dim strValue As String
			If dateValue.HasValue Then
				strValue = dateValue.Value.ToString("yyyyMMdd")
			Else
				strValue = Nothing
			End If
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, strValue)
		End Sub

		Protected Shared Function Read_DBASE3_DATE_DateTime(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As DateTime
		'ref string stringValue
			' Using this for generating an index speeds it up by 50%. No checks added, assuming data is correct
			Dim year As Integer = (srcBytes(offset) - 48) * 1000 + (srcBytes(offset + 1) - 48) * 100 + (srcBytes(offset + 2) - 48) * 10 + (srcBytes(offset + 3) - 48)
			If year = 0 Then
				Return DateTime.MinValue
			End If

			Dim month As Integer = (srcBytes(offset + 4) - 48) * 10 + (srcBytes(offset + 5) - 48)
			If month = 0 Then
				Return DateTime.MinValue
			End If

			Dim day As Integer = (srcBytes(offset + 6) - 48) * 10 + (srcBytes(offset + 7) - 48)
			If day = 0 Then
				Return DateTime.MinValue
			End If

			Try
				Return New DateTime(year, month, day)
			Catch generatedExceptionName As Exception

				Return New DateTime()
			End Try
		End Function

		Protected Shared Sub Write_DateTime_DBASE3_DATE(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, dateValue As DateTime)
			Dim value As Integer = dateValue.Year
			dstBytes(dst) = CByte(48 + (value \ 1000) Mod 10)
			dstBytes(dst + 1) = CByte(48 + (value \ 100) Mod 10)
			dstBytes(dst + 2) = CByte(48 + (value \ 10) Mod 10)
			dstBytes(dst + 3) = CByte(48 + value Mod 10)
			value = dateValue.Month
			dstBytes(dst + 4) = CByte(48 + (value \ 10) Mod 10)
			dstBytes(dst + 5) = CByte(48 + value Mod 10)
			value = dateValue.Day
			dstBytes(dst + 6) = CByte(48 + (value \ 10) Mod 10)
			dstBytes(dst + 7) = CByte(48 + value Mod 10)
		End Sub


		Protected Shared Function Read_DATETIME_DateTime(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As DateTime
		'ref string stringValue
			Dim ticks As Int64 = BitConverter.ToInt64(srcBytes, offset)
			Return New DateTime(ticks)
		End Function

		Protected Shared Sub Write_DateTime_DATETIME(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, dateValue As DateTime)
			Dim ticks As Int64 = dateValue.Ticks

			Dim srcBytes As [Byte]() = BitConverter.GetBytes(ticks)
			WriteInBytes(dstBytes, dst, srcBytes)
		End Sub


		Protected Shared Function Read_BYTE_Byte(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Byte
			Return srcBytes(offset)
		End Function

		Protected Shared Sub Write_Byte_BYTE(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, byteValue As [Byte])
			WriteSingleByte(dstBytes, dst, byteValue)
		End Sub

		Protected Shared Function Read_DBASE3_LOGICAL_NullableBoolean(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As System.Nullable(Of [Boolean])
			Select Case ChrW(srcBytes(offset))
				Case "T"C, "t"C, "Y"C, "y"C
					Return True
				Case "F"C, "f"C, "N"C, "n"C
					Return False
				Case Else
					Return Nothing
				'throw new Exception("Invalid Logical Value");
			End Select
		End Function

		Protected Shared Sub Write_NullableBoolean_DBASE3_LOGICAL(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As System.Nullable(Of [Boolean]))
			Dim val As Char
			If value.HasValue Then
				val = (If(value.Value, "T"C, "F"C))
			Else
				val = " "C
			End If
			WriteSingleByte(dstBytes, dst, CType(AscW(val), [Byte]))
		End Sub

		Protected Shared Function Read_DBASE3_LOGICAL_Boolean(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As [Boolean]
			Dim v As Char = ChrW(srcBytes(offset))
			Select Case v
				Case ChrW(1), "Y"C, "y"C, "T"C, "t"C
					Return True
				Case " "C, ControlChars.NullChar, "N"C, "n"C, "F"C, "f"C
					Return False
				Case Else
					Throw New Exception("Invalid Logical Value")
			End Select
		End Function

		Protected Shared Sub Write_Boolean_DBASE3_LOGICAL(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As [Boolean])
			Dim val As Char = (If(value, "T"C, "F"C))
			WriteSingleByte(dstBytes, dst, CType(AscW(val), [Byte]))
		End Sub

		Protected Shared Function Read_DELETED_FLAG_Boolean(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As [Boolean]
			Dim value As Byte = srcBytes(offset)
			Select Case value
				Case 1
					Return True
				Case 0, 32
					' space seems to be tolerated
					Return False
				Case Else
					Throw New Exception("Invalid Logical Value")
			End Select
		End Function

		Protected Shared Sub Write_Boolean_DELETED_FLAG(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As [Boolean])
			Dim val As Char = (If(value, ChrW(1), ControlChars.NullChar))
			WriteSingleByte(dstBytes, dst, CType(AscW(val), [Byte]))
		End Sub


		Protected Shared Function Read_INT32_Int32(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Int32
            Dim result As Byte = srcBytes(offset) Or (srcBytes(offset + 1) << 8) Or (srcBytes(offset + 2) << 16) Or (srcBytes(offset + 3) << 24)

			Return result
		End Function

		Protected Shared Sub Write_Int32_INT32(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Int32)
			WriteSingleByte(dstBytes, dst, CByte(value And &Hff))
			WriteSingleByte(dstBytes, dst + 1, CByte((value >> 8) And &Hff))
			WriteSingleByte(dstBytes, dst + 2, CByte((value >> 16) And &Hff))
			WriteSingleByte(dstBytes, dst + 3, CByte((value >> 24) And &Hff))
		End Sub
        Protected Shared Function Read_UINT32_UInt32(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Int32
            Dim result As Int32 = CType(srcBytes(offset) Or (srcBytes(offset + 1) << 8) Or (srcBytes(offset + 2) << 16) Or (srcBytes(offset + 3) << 24), Int32)
            Return result
        End Function
        Protected Shared Sub Write_UInt32_UINT32(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Int32)
            WriteSingleByte(dstBytes, dst, CByte(value And &HFF))
            WriteSingleByte(dstBytes, dst + 1, CByte((value >> 8) And &HFF))
            WriteSingleByte(dstBytes, dst + 2, CByte((value >> 16) And &HFF))
            WriteSingleByte(dstBytes, dst + 3, CByte((value >> 24) And &HFF))
        End Sub
		Protected Shared Function Read_INT16_Int16(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Int16
			Return CType(srcBytes(offset) Or (srcBytes(offset + 1) << 8), Int16)
		End Function

		Protected Shared Sub Write_Int16_INT16(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Int16)
			WriteSingleByte(dstBytes, dst, CByte(value And &Hff))
			WriteSingleByte(dstBytes, dst + 1, CByte((value >> 8) And &Hff))
		End Sub

		Protected Shared Function Read_UINT16_UInt16(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As UInt16
			Return CType(srcBytes(offset) Or (srcBytes(offset + 1) << 8), UInt16)
		End Function

		Protected Shared Sub Write_UInt16_UINT16(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As UInt16)
			WriteSingleByte(dstBytes, dst, CByte(value And &Hff))
			WriteSingleByte(dstBytes, dst + 1, CByte((value >> 8) And &Hff))
		End Sub

		Protected Shared Function Read_DBASE3_NUMBER_NullableDouble(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As System.Nullable(Of Double)
			Dim valueStr As String = Read_DBASE3_CHARACTER_String(encoding, srcBytes, offset, length).TrimEnd()
			If valueStr.Length = 0 Then
				Return Nothing
			End If
			Return Double.Parse(valueStr)
		End Function

		Protected Shared Sub Write_NullableDouble_DBASE3_NUMBER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As System.Nullable(Of Double))
			Dim valueStr As String
			If value.HasValue Then
				valueStr = value.ToString()
			Else
				valueStr = Nothing
			End If
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, valueStr)
		End Sub

		Protected Shared Function Read_DBASE3_NUMBER_Double(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Double
			Dim valueStr As String = Read_DBASE3_CHARACTER_String(encoding, srcBytes, offset, length).TrimEnd()
			If valueStr.Length = 0 Then
				Return 0
			End If
			Return Double.Parse(valueStr)
		End Function

		Protected Shared Sub Write_Double_DBASE3_NUMBER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Double)
			Dim valueStr As String
			valueStr = value.ToString()
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, valueStr)
		End Sub

		Protected Shared Function Read_DBASE4_FLOAT_Double(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Double
			Dim valueStr As String = Read_DBASE3_CHARACTER_String(encoding, srcBytes, offset, length).TrimEnd()
			If valueStr.Length = 0 Then
				Return 0
			End If
			Dim result As Double
			If Double.TryParse(valueStr, result) Then
				Return result
			Else
				Return 0
			End If
		End Function

		Protected Shared Sub Write_Double_DBASE4_FLOAT(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Double)
			Dim valueStr As String
			valueStr = value.ToString()
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, valueStr)
		End Sub


		Protected Shared Function Read_DBASE3_NUMBER_NullableInt32(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As System.Nullable(Of Int32)
			Dim valueStr As String = Read_DBASE3_CHARACTER_String(encoding, srcBytes, offset, length)
			If valueStr.Length = 0 Then
				Return Nothing
			Else
				Return Int32.Parse(valueStr)
			End If
		End Function

		Protected Shared Sub Write_NullableInt32_DBASE3_NUMBER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As System.Nullable(Of Int32))
			Dim valueStr As String = value.ToString()
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, valueStr)
		End Sub


		Protected Shared Function Read_DBASE3_NUMBER_Int32(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As Int32
			Dim valueStr As String = Read_DBASE3_CHARACTER_String(encoding, srcBytes, offset, length)
			Return Int32.Parse(valueStr)
		End Function

		Protected Shared Sub Write_Int32_DBASE3_NUMBER(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As Int32)
			Dim valueStr As String = value.ToString()
			Write_String_DBASE3_CHARACTER(dstBytes, encoding, columnIndex, dst, dstLength, valueStr)
		End Sub

		Protected Shared Function Read_DBASE3_MEMO_String(encoding As IHasEncoding, srcBytes As [Byte](), offset As Integer, length As Integer) As String
			Dim val As System.Nullable(Of Integer) = Read_DBASE3_NUMBER_NullableInt32(encoding, srcBytes, offset, length)
			' todo read the memo file...
			If val.HasValue Then
					' BitConverter.ToDouble(srcBytes, offset);
				Return val.ToString()
			Else
				Return Nothing
			End If
		End Function

		Protected Shared Sub Write_String_DBASE3_MEMO(dstBytes As [Byte](), encoding As IHasEncoding, columnIndex As Integer, dst As Integer, dstLength As Integer, value As String)
			' Byte[] srcBytes = BitConverter.GetBytes(value);
			' WriteInBytes(dstBytes, dst, srcBytes);
		End Sub

		#End Region

	End Class


End Namespace
